encodeURI 和 encodeURIComponent 都用于对字符串进行百分号编码,但它们面向的对象不同:
encodeURI:编码一个完整的 URI,同时尽量保留 URI 的结构符号。encodeURIComponent:编码 URI 中的一个组成部分,例如查询参数的值。
所以,编码 https://example.com/a?x=1&y=2 时,两者结果不同是正常的。
一、先看编码结果
const url = "https://example.com/a?x=1&y=2";
console.log(encodeURI(url));
// https://example.com/a?x=1&y=2
console.log(encodeURIComponent(url));
// https%3A%2F%2Fexample.com%2Fa%3Fx%3D1%26y%3D2
encodeURI 编码后,URL 看起来还是一个完整的 URL,这是因为它保留了 URL 中用于分隔结构的字符,例如:
: / ? & =
而 encodeURIComponent 会把这些字符也编码掉。结果中的冒号、斜杠、问号、等号和 & 都变成了百分号编码。
因此,encodeURIComponent 的结果看起来不再像一个可直接使用的 URL。它表示的是“整个 URL 字符串作为一个数据片段”,而不是保留 URL 结构后的地址。
二、为什么需要两种方法?
URL 不只是普通字符串,它有自己的结构。例如:
https://example.com/search?q=JavaScript&sort=recent
可以拆成:
协议: https:
主机名: example.com
路径: /search
参数分隔符: ?
参数: q=JavaScript
参数分隔符: &
参数: sort=recent
在这个结构里,?、&、=、/ 等字符可能具有特殊含义。如果把它们全部保留或全部编码,结果会完全不同。
encodeURI 适合处理整个 URI:尽可能保留这些结构符号。
encodeURIComponent 适合处理URI 的某一个值:将值中的结构符号也编码,避免它们被误认为 URL 的分隔符。
三、encodeURI 会保留哪些字符?
encodeURI 通常会保留 URL 中有结构意义的字符,包括:
; , / ? : @ & = + $ # -
例如:
encodeURI("https://example.com/search?q=hello world");
结果:
https://example.com/search?q=hello%20world
URL 的结构部分仍然保留,而空格被编码成 %20。
encodeURI 的一个重要限制
encodeURI 会保留 #,但 # 在 URL 中表示片段标识符。因此,字符串中的 # 可能会被浏览器当成 URL 片段的开始。
const url = "https://example.com/search#tag";
console.log(encodeURI(url));
// https://example.com/search#tag
如果 # 本来是某个参数值中的普通字符,就不应该直接依赖 encodeURI 来处理整个 URL。应当对参数值单独编码。
四、encodeURIComponent 会做什么?
encodeURIComponent 会编码 URI 中具有结构意义的分隔符,例如:
; , / ? : @ & = + $ #
例如:
const value = "https://example.com/a?x=1&y=2";
console.log(encodeURIComponent(value));
// https%3A%2F%2Fexample.com%2Fa%3Fx%3D1%26y%3D2
在这里,整个字符串被作为一个普通值处理。原有的 https://、? 和 & 不再作为 URL 结构符号存在。
五、最常见的用法:编码查询参数的值
假设要把用户输入放进查询参数:
const keyword = "JavaScript & 前端";
const url = "https://example.com/search?q=" + encodeURIComponent(keyword);
console.log(url);
// https://example.com/search?q=JavaScript%20%26%20%E5%89%8D%E7%AB%AF
这里应该使用 encodeURIComponent,因为 keyword 是参数值。
如果错误地使用 encodeURI:
const keyword = "JavaScript & sort=recent";
const url = "https://example.com/search?q=" + encodeURI(keyword);
& 和 = 会被保留,拼出来的内容可能被解释成额外的查询参数:
https://example.com/search?q=JavaScript%20&%20sort=recent
这样就可能改变 URL 的含义。
六、简单记忆
可以这样记:
处理完整 URL,考虑使用
encodeURI;处理 URL 中的某一个值,使用encodeURIComponent。
示例:
const fullUrl = "https://example.com/search?q=hello world";
// 整个 URL
encodeURI(fullUrl);
// 参数值
encodeURIComponent("hello world");
不过,实际项目中,拼接查询参数时通常更推荐使用 URLSearchParams,避免手动处理编码和分隔符。
七、推荐做法:使用 URLSearchParams
const params = new URLSearchParams({
q: "JavaScript & 前端",
sort: "recent",
});
const url = `https://example.com/search?${params.toString()}`;
console.log(url);
// https://example.com/search?q=JavaScript+%26+%E5%89%8D%E7%AB%AF&sort=recent
URLSearchParams 会处理参数值的编码,并正确构造查询字符串。对于普通的查询参数场景,通常比手动拼接字符串更安全、清晰。
也可以基于 URL 对象构造地址:
const url = new URL("https://example.com/search");
url.searchParams.set("q", "JavaScript & 前端");
url.searchParams.set("sort", "recent");
console.log(url.toString());
八、空格编码:%20 与 +
encodeURI 和 encodeURIComponent 通常会把空格编码成:
%20
而 URLSearchParams 在序列化查询参数时,可能会把空格表示为:
+
例如:
new URLSearchParams({ q: "hello world" }).toString();
// q=hello+world
这两种形式常见于不同的 URL 编码场景。处理查询参数时,优先使用 URLSearchParams,不要为了统一格式而随意手动替换。
九、几个常见误区
1. 不要用 encodeURIComponent 编码整个 URL 后直接访问
const encoded = encodeURIComponent("https://example.com");
结果会变成:
https%3A%2F%2Fexample.com
这已经不是普通的 https://example.com URL 结构了。它更适合作为另一个 URL 的参数值,例如:
const redirectUrl = "https://example.com/login?next=" +
encodeURIComponent("https://example.com/dashboard");
2. 不要重复编码
const once = encodeURIComponent("hello world");
// hello%20world
const twice = encodeURIComponent(once);
// hello%2520world
第二次编码时,% 被编码成了 %25,于是 %20 变成 %2520。这通常会导致数据恢复时出现问题。
3. 编码不等于安全过滤
URI 编码的目的是正确表示字符串,不是替代输入校验、权限检查或防止所有注入攻击。安全处理仍应结合具体使用场景完成。
总结
方法 | 处理对象 | 是否保留 URL 结构符号 | 常见用途 |
|---|---|---|---|
| 完整 URI | 是 | 编码完整 URL 中需要编码的字符 |
| URI 的一个组成部分 | 否,更多分隔符会被编码 | 编码查询参数值、路径参数值等 |
简单来说,encodeURI("https://...") 编码后仍像一个 URL,是因为它保留了 URL 的结构字符;encodeURIComponent("https://...") 会把这些结构字符也编码,因此结果看起来像一整段普通数据。查询参数的值优先使用 URLSearchParams 或 encodeURIComponent。